﻿using System;
using System.IO;
using System.IO.Compression;
using System.Text;

namespace personalplaner.core.zip
{
	public class GZipCompress
	{
		public static GZipResult Compress(string lpSourceFolder, /*string searchPattern,*/ SearchOption searchOption, string lpDestFolder, string zipFileName, bool deleteTempFile)
		{
			DirectoryInfo di = new DirectoryInfo(lpSourceFolder);
			FileInfo[] files = di.GetFiles("*.*", searchOption);
			return Compress(files, lpSourceFolder, lpDestFolder, zipFileName, deleteTempFile);
		}

		public static GZipResult Compress(FileInfo[] files, string lpBaseFolder, string lpDestFolder, string zipFileName, bool deleteTempFile)
		{
			GZipResult result = new GZipResult();

			if (!lpDestFolder.EndsWith("\\"))
			{
				lpDestFolder += "\\";
			}

			string lpTempFile = lpDestFolder + zipFileName + ".tmp";
			string lpZipFile = lpDestFolder + zipFileName;

			result.TempFile = lpTempFile;
			result.ZipFile = lpZipFile;

			//int fileCount = 0;

			if (files != null && files.Length > 0)
			{
				CreateTempFile(files, lpBaseFolder, lpTempFile, result);

				if (result.FileCount > 0)
				{
					CreateZipFile(lpTempFile, lpZipFile, result);
				}

				// delete the temp file
				try
				{
					if (deleteTempFile)
					{
						File.Delete(lpTempFile);
						result.TempFileDeleted = true;
					}
				}
				catch (Exception ex4)
				{
					// handle or display the error 
					throw ex4;
				}
			}
			return result;
		}

		private static void CreateZipFile(string lpSourceFile, string lpZipFile, GZipResult result)
		{
			byte[] buffer;
			int count = 0;
			FileStream fsOut = null;
			FileStream fsIn = null;
			GZipStream gzip = null;

			// compress the file into the zip file
			try
			{
				fsOut = new FileStream(lpZipFile, FileMode.Create, FileAccess.Write, FileShare.None);
				gzip = new GZipStream(fsOut, CompressionMode.Compress, true);

				fsIn = new FileStream(lpSourceFile, FileMode.Open, FileAccess.Read, FileShare.Read);
				buffer = new byte[fsIn.Length];
				count = fsIn.Read(buffer, 0, buffer.Length);
				fsIn.Close();
				fsIn = null;

				// compress to the zip file
				gzip.Write(buffer, 0, buffer.Length);

				result.ZipFileSize = fsOut.Length;
				//result.CompressionPercent = GetCompressionPercent(result.TempFileSize, result.ZipFileSize);
			}
			catch (Exception ex1)
			{
				// handle or display the error 
				throw ex1;
			}
			finally
			{
				if (gzip != null)
				{
					gzip.Close();
					gzip = null;
				}
				if (fsOut != null)
				{
					fsOut.Close();
					fsOut = null;
				}
				if (fsIn != null)
				{
					fsIn.Close();
					fsIn = null;
				}
			}
		}

		public static void CreateTempFile(FileInfo[] files, string lpBaseFolder, string lpTempFile, GZipResult result)
		{
			byte[] buffer;
			int count = 0;
			byte[] header;
			string fileHeader = null;
			string fileModDate = null;
			string lpFolder = null;
			int fileIndex = 0;
			string lpSourceFile = null;
			string vpSourceFile = null;
			GZippedFile gzf = null;
			FileStream fsOut = null;
			FileStream fsIn = null;

			if (files != null && files.Length > 0)
			{
				try
				{
					result.Files = new GZippedFile[files.Length];

					// open the temp file for writing
					fsOut = new FileStream(lpTempFile, FileMode.Create, FileAccess.Write, FileShare.None);

					foreach (FileInfo fi in files)
					{
						lpFolder = fi.DirectoryName + "\\";
						try
						{
							gzf = new GZippedFile();
							gzf.Index = fileIndex;

							//Console.WriteLine("fi.DirectoryName: {0}", fi.DirectoryName);
							//Console.WriteLine("fi.FullName: {0}", fi.FullName);
							// read the source file, get its virtual path within the source folder
							lpSourceFile = fi.FullName;
							gzf.LocalPath = lpSourceFile;
							vpSourceFile = lpSourceFile.Replace(lpBaseFolder, string.Empty);
							vpSourceFile = vpSourceFile.Replace("\\", "/");
							gzf.RelativePath = vpSourceFile;


							//Console.WriteLine("lpBaseFolder: {0}", lpBaseFolder);
							//Console.WriteLine("vpSourceFile: {0}", vpSourceFile);

							fsIn = new FileStream(lpSourceFile, FileMode.Open, FileAccess.Read, FileShare.Read);
							buffer = new byte[fsIn.Length];
							count = fsIn.Read(buffer, 0, buffer.Length);
							fsIn.Close();
							fsIn = null;

							fileModDate = fi.LastWriteTimeUtc.ToString();
							gzf.ModifiedDate = fi.LastWriteTimeUtc;
							gzf.Length = buffer.Length;

							fileHeader = fileIndex.ToString() + "," + vpSourceFile + "," + fileModDate + "," + buffer.Length.ToString() + "\n";
							header = Encoding.Default.GetBytes(fileHeader);

							fsOut.Write(header, 0, header.Length);
							fsOut.Write(buffer, 0, buffer.Length);
							fsOut.WriteByte(10); // linefeed

							gzf.AddedToTempFile = true;

							// update the result object
							result.Files[fileIndex] = gzf;

							// increment the fileIndex
							fileIndex++;
						}
						catch (Exception ex1)
						{
							// handle or display the error 
							throw ex1;
						}
						finally
						{
							if (fsIn != null)
							{
								fsIn.Close();
								fsIn = null;
							}
						}
						if (fsOut != null)
						{
							result.TempFileSize = fsOut.Length;
						}
					}
				}
				catch (Exception ex2)
				{
					// handle or display the error 
					throw ex2;
				}
				finally
				{
					if (fsOut != null)
					{
						fsOut.Close();
						fsOut = null;
					}
				}
			}

			result.FileCount = fileIndex;
		}
	}
}
